package concurrency.prod_cons.semaphore;

import java.util.concurrent.Semaphore;

public class ProducteurConsommateur {

	private boolean stop = false;
	private int repetition;
	
	// ============  DEBUT VAR PARTAGEES ============  
	private final String[] tampon;
	private final int N;
	private int tete;
	private int queue;

	private Semaphore semNPlein;
	private Semaphore semNVide;
	// ============  FIN VAR PARTAGEES ============  

	public ProducteurConsommateur(int N, final int repetition) {
		this.repetition = repetition;
		// ============  DEBUT INITIALISATIONS============  
		this.N = N; // Taille du tampon
		tampon = new String[N];
		tete = 0;
		queue = 0;

		semNPlein = new Semaphore(0, true);
		semNVide = new Semaphore(N, true); // N cases vides initiales
		// ============  FIN INITIALISATIONS ============  
	}
	
	public void launch() {
		Thread t = new Thread() {
			public void run() {
				for (int i = 0; i < repetition; i++) {
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				stop = true;
			};
		};
		t.start();
	}

	// ============  PROCESSUS PRODUCTEUR ============  
	private class Producteur extends Thread {

		public Producteur(String name) {
			setName(name + " <- PRODUCTEUR" + " " + super.getName());
		}

		@Override
		public void run() {
			try {
				while (!stop) {
					String message = "" + (int) (Math.random() * 1000); // Production du message
					// ============  
					semNVide.acquire(); // P(NVide)
					// ============ DEBUT SC ============  
					tampon[queue] = message;
					System.out.println(Thread.currentThread().getName()
							+ " depose    [" + queue + "]" + message); // Depot du message
					queue = (queue + 1) % N;
					// ============  
					semNPlein.release(); // V(NPlein)
					// ============ FIN SC ============  
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	// ============  PROCESSUS CONSOMMATEUR ============  
	private class Consommateur extends Thread {

		public Consommateur(String name) {
			setName(name + " <- CONSOMMATEUR" + " " + super.getName());
		}

		@Override
		public void run() {
			try {
				while (!stop) {
					// ============  
					semNPlein.acquire(); // P(NPlein)
					// ============ DEBUT SC ============  
					String message = tampon[tete];
					tete = (tete + 1) % N;
					// ============  
					semNVide.release(); // V(NVide)
					// ============ FIN SC ============  
					System.out.println(Thread.currentThread().getName()
							+ " consomme [" + tete + "]" + message);// Consommation du message
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) {
		int repetition = 10; // Nombre de productions et de consommations
		int N = 10; // Taille tampon
		ProducteurConsommateur varPartagee = new ProducteurConsommateur(N, repetition);
		Consommateur cons1 = varPartagee.new Consommateur("Pierre");
		Consommateur cons2 = varPartagee.new Consommateur("Paul");
		Consommateur cons3 = varPartagee.new Consommateur("Jacques");
		Producteur prod1 = varPartagee.new Producteur("Jean");
		Producteur prod2 = varPartagee.new Producteur("Yo");
		prod1.start();
		prod2.start();
		cons1.start();
		cons2.start();
		cons3.start();
		varPartagee.launch();
		try {
			prod1.join();
			prod2.join();
			cons1.join();
			cons2.join();
			cons3.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
